/***
    Amrita ITEWS - Copyright (C) 2009 Amrita Vishwa Vidyapeetham, Amritapuri.
                                       (http://www.amrita.edu)
    ***************************************************************************
    This file is part of the Amrita ITEWS distribution.
    Amrita ITEWS is free software; you can redistribute it and/or modify it 
    under the terms of the GNU General Public License (version 2) as published
    by the Free Software Foundation AND MODIFIED BY the Amrita ITEWS exception.
    ***NOTE*** The exception to the GPL is included to allow you to distribute
    a combined work that includes Amrita ITEWS without being obliged to provide
    the source code for proprietary components outside of the Amrita ITEWS
    software. Amrita ITEWS is distributed in the hope that it will be useful, 
    but WITHOUT ANY WARRANTY; without even the implied warranty of 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
    Public License for more details. You should have received a copy of the GNU
    General Public License and the Amrita ITEWS license exception along with 
    Amrita ITEWS if not then it can be viewed here: 
    http://itews.amrita.ac.in/license.html.

    Documentation, latest information, license and contact details are at:
    http://itews.amrita.ac.in/

    Amrita ITEWS source code can be found at:
    http://code.google.com/p/itews

    The initial code-base of Amrita ITEWS was developed by Amrita Vishwa 
    Vidyapeetham as part of the project titled,"Intelligent & Interactive 
    Telematics using Emerging Wireless Technologies for Transport Systems 
    (ITEWS)" sponsored by Technology Information Forecasting and Assessment 
    Council (TIFAC), India.
***/

package baseplatformservices;

import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.ConnectException;
import java.net.HttpRetryException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.NoRouteToHostException;
import java.net.UnknownHostException;
import java.net.UnknownServiceException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import org.apache.commons.httpclient.ConnectionPoolTimeoutException;
import org.apache.commons.httpclient.CircularRedirectException;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.NoHttpResponseException;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.ConnectTimeoutException;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpConnectionManager;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
import org.ho.yaml.Yaml;
import org.jdom.adapters.JAXPDOMAdapter;
import org.knopflerfish.service.log.LogRef;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class BlogSubscriber extends Thread
{
	private EventAdmin eventAdmin;
	private BasePlatformConfig config;
	private boolean running = true;
	private DBConnectionPool dbConnectionPool ;
	private HttpClient client;
	private String feedURL = null;
	private  LogRef log;
	private ArrayList listOfMsgsToBeSentToApplication;
	private String eTagQuery = "SELECT etag, timestamp from etag_table where app_name = ?";
	private String eTagDelete = "DELETE FROM etag_table";
	public BlogSubscriber(EventAdmin eventAdmin) throws Exception
	{
		Connection conn = null;
		PreparedStatement preparedStmt1 = null;
		try 
		{
			listOfMsgsToBeSentToApplication = new ArrayList();
			log = new LogRef(Activator.bc);
			this.eventAdmin = eventAdmin;
			client = new HttpClient();
			config = BasePlatformConfig.getConfig();
			HttpConnectionManagerParams hcp = new HttpConnectionManagerParams();
			hcp.setConnectionTimeout(config.getHttpConnTimeoutSecs()*1000);
			hcp.setSoTimeout(config.getHttpSocTimeoutSecs()*1000);
			HttpConnectionManager connManager = client.getHttpConnectionManager();
			connManager.setParams(hcp);
			dbConnectionPool = DBConnectionPool.getInstance(config.getJdbcConnectionURI());
			feedURL = config.getFeedURL();
			conn = dbConnectionPool.getConnection();
			preparedStmt1 = dbConnectionPool.getPreparedStmt(conn, eTagDelete);
			preparedStmt1.executeUpdate();
			preparedStmt1.close(); 
			preparedStmt1 = null;
			conn.close();
			conn = null;
		} 
		catch (Exception e) 
		{
			log.error("Exception while initiating BlogSubscriber Service");
    		if (preparedStmt1 != null)
    		{
    			try {preparedStmt1.close();} catch (SQLException e1) {} 
    			preparedStmt1 = null;
    		}
			if (conn != null)
			{
				try {conn.close();} catch (SQLException e1) {}
				conn = null;
			}
			throw e;
		} 
	}
	
	public void run() 
	{
		System.out.println("Blog Subscriber started");
		while (running)
		{
			Connection conn = null;
			GetMethod getMethod = null;
			PreparedStatement preparedStmt1 = null;
			ResultSet rs1 = null;
			String app = null;
			Long eTagValue = null;
			Long timeStampValue = null;
			boolean exceptionOccurred = false;
			try 
			{
				if (feedURL != null)
				{
					String[] blogSubscriberApps = config.getBlogSubscriberApps();
					conn = dbConnectionPool.getConnection();
					if (conn != null)
					{
						conn.setAutoCommit(false);
						for (int i=0; i<blogSubscriberApps.length; i++)
						{
				        	listOfMsgsToBeSentToApplication.clear();
							app = (String) blogSubscriberApps[i];
							getMethod = new GetMethod(feedURL + app + "/" + config.getTelematicsUnit());
							preparedStmt1 = dbConnectionPool.getPreparedStmt(conn, eTagQuery);
							preparedStmt1.setString(1, app);
							rs1 = preparedStmt1.executeQuery();
							Long dataBaseETag = 0L;
							Long dataBaseTimestamp = 0L;
							if (rs1.next() == true)
							{
								dataBaseETag = rs1.getLong("etag");
								dataBaseTimestamp = rs1.getLong("timestamp");
							}
							rs1.close();
							rs1 = null;
							preparedStmt1.close(); 
							preparedStmt1 = null;
							getMethod.addRequestHeader(new Header("If-None-Match", String.valueOf(dataBaseETag)));
							getMethod.addRequestHeader(new Header("If-Modified-Since", String.valueOf(dataBaseTimestamp)));
							client.executeMethod(getMethod);
							if (getMethod.getStatusCode() == HttpStatus.SC_OK) 
							{
								eTagValue = Long.parseLong(getMethod.getResponseHeader(Activator.E_TAG).getValue().trim());
	        		        	timeStampValue = Long.parseLong(getMethod.getResponseHeader(Activator.TIME_STAMP).getValue().trim());
								InputStream inputStream = getMethod.getResponseBodyAsStream();
					        	JAXPDOMAdapter domAdapter = new JAXPDOMAdapter();
								Document doc = domAdapter.getDocument(inputStream, false);
							  	Node feed = doc.getFirstChild();
					        	NodeList nodeList = feed.getChildNodes();
					        	//System.out.println(getMethod.getResponseBodyAsString());
					        	for (int j=0; j< nodeList.getLength(); j++)
					        	{
					        		Node node = nodeList.item(j);
					        		if (node.getNodeName().equals("entry"))
					        		{
					        			NodeList childList = node.getChildNodes();
				        				String content = null;
				        				String category = null;
				        				String msgId = null;
					        			for(int k=0;k<childList.getLength();k++)
					        			{
					        				Node child = childList.item(k);
					        				if (child.getNodeName().equals("content"))
					        				{
					        					content = child.getTextContent();
					        				}
					        				if (child.getNodeName().equals("category"))
					        				{
					        					category = child.getTextContent().trim();
					        				}
					        				if (child.getNodeName().equals("id"))
					        				{
					        					msgId = child.getTextContent().trim();
					        				}
					        				if (content != null)
					        				{
					        		        	HashMap map =(HashMap) Yaml.load(content);
						        		        map.put(Activator.MSG_ID, msgId.trim());
						        		        map.put("category", category);
					        		        	Hashtable appDetails = new Hashtable(map);
					        		        	listOfMsgsToBeSentToApplication.add(appDetails);
					        					System.out.println("Blog Subscriber: Received content after GET of "+ feedURL + app+ "/" + config.getTelematicsUnit() + " for category: "+category);
					        					content = null;
					        				}
					        			}
					        		}
					        	}
					        	for (int j=0; j < listOfMsgsToBeSentToApplication.size(); j++)
					        	{
			        		        Hashtable appDetails = (Hashtable) listOfMsgsToBeSentToApplication.get(j);
			        		        String category = (String) appDetails.remove("category");
			        		        System.out.println("Blog Subscriber: Posted event with topic: "+ category);
		        					Event event = new Event(category, appDetails);
		        					this.eventAdmin.postEvent(event);
					        	}
								if (app != null && timeStampValue != null && eTagValue != null && timeStampValue!=0L && eTagValue != 0L)
								{
									Hashtable table = new Hashtable();
									table.put(Activator.APP_SERVICE_NAME, app);
						        	table.put(Activator.E_TAG, eTagValue);
						        	table.put(Activator.TIME_STAMP, timeStampValue);
									Event eventToBasePlatformService = new Event(config.getEventTopicToBPSBundle(), table);
									this.eventAdmin.postEvent(eventToBasePlatformService);
								}
					        }
							else if (getMethod.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) 
							{
								//log.info("BlogSubscriber:" + feedURL + app + " Request. HTTP Status: NOT Modified ");
								//do nothing.
							}
							else if (getMethod.getStatusCode() == HttpStatus.SC_REQUEST_TIMEOUT |
									getMethod.getStatusCode() == HttpStatus.SC_GATEWAY_TIMEOUT |
									getMethod.getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE) 
							{
								i = 0;
								log.warn("BlogSubscriber: Service unavailable during GET  "+ feedURL + app + "/" + config.getTelematicsUnit() + " Request. HTTP Status: "+getMethod.getStatusCode());
							}
							else
							{
								//log.info("BlogSubscriber: " +getMethod.getResponseBodyAsString().trim() +" for feed URL: "+ feedURL + app + "/" + config.getTelematicsUnit()  + ". HTTP Status Code: "+ getMethod.getStatusCode());
							}
							getMethod.releaseConnection();
							getMethod = null;
							conn.commit();
						}
					}
					else
					{
						log.error("Blog Subscriber: No connection available to database");
						try { Thread.sleep(2000);} catch (InterruptedException e1) {}
					}
				}
			}
			catch (NoRouteToHostException nre) 
			{
				log.debug(nre + " URL: " +feedURL + app + "/" + config.getTelematicsUnit());
				exceptionOccurred = true;
			}
			catch (MalformedURLException mue) 
			{
				log.debug(mue + " URL: " +feedURL + app + "/" + config.getTelematicsUnit());
				exceptionOccurred = true;
			}
			catch (ConnectException ce) 
			{
				log.debug(ce + " URL: " +feedURL + app + "/" + config.getTelematicsUnit());
				exceptionOccurred = true;
			}
			catch (HttpRetryException hre) 
			{
				log.debug(hre + " URL: " +feedURL + app + "/" + config.getTelematicsUnit());
				exceptionOccurred = true;
			}
			catch (SocketException ste) 
			{
				log.debug(ste + " URL: " +feedURL + app + "/" + config.getTelematicsUnit());
				exceptionOccurred = true;
			}
			catch (SocketTimeoutException ste) 
			{
				log.debug(ste + " URL: " +feedURL + app + "/" + config.getTelematicsUnit());
				exceptionOccurred = true;
			}
			catch (UnknownServiceException use) 
			{
				log.debug(use + " URL: " +feedURL + app + "/" + config.getTelematicsUnit());
				exceptionOccurred = true;
			}
			catch (UnknownHostException uhe) 
			{
				log.debug(uhe + " URL: " +feedURL + app + "/" + config.getTelematicsUnit());
				exceptionOccurred = true;
			}
			catch (ConnectTimeoutException cte) 
			{
				log.debug(cte + " URL: " +feedURL + app + "/" + config.getTelematicsUnit());
				exceptionOccurred = true;
			}
			catch (CircularRedirectException cre) 
			{
				log.debug(cre + " URL: " +feedURL + app + "/" + config.getTelematicsUnit());
				exceptionOccurred = true;
			}
			catch (HttpException he) 
			{
				log.debug(he + " URL: " +feedURL + app + "/" + config.getTelematicsUnit());
				exceptionOccurred = true;
			}
			catch (NoHttpResponseException nhe) 
			{
				log.debug(nhe + " URL: " +feedURL + app + "/" + config.getTelematicsUnit());
				exceptionOccurred = true;
			}
			catch (Exception e) 
			{
				log.debug("BlogSubscriber error while subscribing to " +feedURL + app + "/" + config.getTelematicsUnit() + " "+ e);
				exceptionOccurred = true;
				e.printStackTrace();
			}
			finally
			{
	    		if (exceptionOccurred == true)
	    		{
					if (app != null && timeStampValue != null && eTagValue != null && timeStampValue!=0L && eTagValue != 0L)
					{
						Hashtable table = new Hashtable();
						table.put(Activator.APP_SERVICE_NAME, app);
			        	table.put(Activator.E_TAG, eTagValue);
			        	table.put(Activator.TIME_STAMP, timeStampValue);
						Event eventToBasePlatformService = new Event(config.getEventTopicToBPSBundle(), table);
						this.eventAdmin.postEvent(eventToBasePlatformService);
					}
					try { Thread.sleep(5000);} catch (InterruptedException e1) {}
	    		}
				if (rs1 != null)
	    		{
	    			try {rs1.close();} catch (SQLException e) {} 
	    			rs1 = null;
	    		}
	    		if (preparedStmt1 != null)
	    		{
	    			try {preparedStmt1.close();} catch (SQLException e) {} 
	    			preparedStmt1 = null;
	    		}
				if (conn != null)
				{
					try {conn.close();} catch (SQLException e) {}
					conn = null;
				}
			}
			try 
			{ 
				Thread.sleep(config.getBlogSubscribeIntervalSecs()*1000 + listOfMsgsToBeSentToApplication.size()*500); 
				
			} 
			catch (InterruptedException e) {}
		}
	}
	
	public void stopThread()
	{
		running = false;
		log.info("Blog Subscriber stopped");
	}
}
